// 源于Inferno utils/6c/txt。c 
// https:
// 
// 版权所有©1994-1999朗讯科技有限公司。保留所有权利。
// 版权所有©1995-1997 C H Forsyth(forsyth@terzarima.net)
// 部分版权©1997-1999维塔诺瓦有限公司
// 部分版权©2000-2007维塔诺瓦控股有限公司（www.vitanuova.com）
// 部分版权©20042006布鲁斯·埃利斯
// 修订版权©2000-2007朗讯科技有限公司及其他
// 部分版权©2009 Go作者。版权所有。
// 
// 特此免费授予获得本软件及相关文档文件（以下简称“软件”）副本
// 的任何人在本软件中不受限制地处理
// 的权利，包括但不限于
// 使用、复制、修改、合并、发布、分发、再许可的权利，和/或出售本软件的
// 副本，并允许提供本软件的人员在以下条件下这样做：
// 
// 上述版权声明和本许可声明应包含在
// 本软件的所有副本或大部分中。
// 
// 本软件按“原样”提供，不提供任何形式的明示或暗示担保，包括但不限于适销性担保、
// 适用于特定用途和非侵权性担保。在任何情况下，
// 作者或版权持有人均不对任何索赔、损害赔偿或其他
// 责任承担责任，无论是合同诉讼、侵权诉讼还是其他诉讼，均由
// 因本软件或本软件的使用或其他交易引起，或与本软件或本软件的使用或其他交易有关。

package objw

import (
	"cmd/compile/internal/base"
	"cmd/compile/internal/ir"
	"cmd/internal/obj"
	"cmd/internal/objabi"
	"cmd/internal/src"
)

var sharedProgArray = new([10000]obj.Prog) // *T代替T解决问题19839 

// /NewProgs为fn返回一个新的Progs。
// worker表示哪个后端工作程序将使用该程序。
func NewProgs(fn *ir.Func, worker int) *Progs {
	pp := new(Progs)
	if base.Ctxt.CanReuseProgs() {
		sz := len(sharedProgArray) / base.Flag.LowerC
		pp.Cache = sharedProgArray[sz*worker : sz*(worker+1)]
	}
	pp.CurFunc = fn

	// 给泵加注油
	pp.Next = pp.NewProg()
	pp.Clear(pp.Next)

	pp.Pos = fn.Pos()
	pp.SetText(fn)
	// PCDATA表隐式地以索引-1开头。
	pp.PrevLive = LivenessIndex{-1, false}
	pp.NextLive = pp.PrevLive
	return pp
}

// Progs为函数积累程序并将其转换为机器代码。
type Progs struct {
	Text       *obj.Prog  // ATEXT Prog用于此函数
	Next       *obj.Prog  // next Prog 
	PC         int64      // virtual PC；程序计数
	Pos        src.XPos   // 用于新程序的位置
	CurFunc    *ir.Func   // fn这些程序用于
	Cache      []obj.Prog // 本地程序缓存
	CacheIndex int        // 程序缓存的第一个自由元素

	NextLive LivenessIndex // 下一个程序的活动度索引
	PrevLive LivenessIndex // 最后发出的活动度索引
}

// 活动度索引存储一个值的活动度映射信息。
type LivenessIndex struct {
	StackMapIndex int

	// IsUnsafePoint表示这是一个不安全的点。
	// 
	// 请注意，调用值可能具有堆栈
	// 映射，同时也是一个不安全的点。这意味着不能在此指令下抢占
	// 但在被调用的函数中可能会发生抢占或
	// 堆栈增长。
	IsUnsafePoint bool
}

// StackMapDontCare表示值为
// 的堆栈映射索引无关紧要。
// 
// 这是一个永远不应发送到PCDATA 
// 流的前哨值。我们使用-1000，因为这显然不是有效的堆栈
// 索引（但-1是）。
const StackMapDontCare = -1000

// LivenessDontCare表示活动性信息无关紧要。目前，当我们不需要它的时候，它会被用来恢复活力。不应将其发送到PCDATA流。
var LivenessDontCare = LivenessIndex{StackMapDontCare, true}

func (idx LivenessIndex) StackMapValid() bool {
	return idx.StackMapIndex != StackMapDontCare
}

func (pp *Progs) NewProg() *obj.Prog {
	var p *obj.Prog
	if pp.CacheIndex < len(pp.Cache) {
		p = &pp.Cache[pp.CacheIndex]
		pp.CacheIndex++
	} else {
		p = new(obj.Prog)
	}
	p.Ctxt = base.Ctxt
	return p
}

// 刷新将pp转换为机器代码。
func (pp *Progs) Flush() {
	plist := &obj.Plist{Firstpc: pp.Text, Curfn: pp.CurFunc}
	obj.Flushplist(base.Ctxt, plist, pp.NewProg, base.Ctxt.Pkgpath)
}

// 免费清除pp和任何相关资源。
func (pp *Progs) Free() {
	if base.Ctxt.CanReuseProgs() {
		// 清除程序以启用GC并避免滥用。
		s := pp.Cache[:pp.CacheIndex]
		for i := range s {
			s[i] = obj.Prog{}
		}
	}
	// 清除pp以避免滥用。
	*pp = Progs{}
}

// Prog添加一个带有pp指令的Prog。
func (pp *Progs) Prog(as obj.As) *obj.Prog {
	if pp.NextLive.StackMapValid() && pp.NextLive.StackMapIndex != pp.PrevLive.StackMapIndex {
		// Emit stack map index change。
		idx := pp.NextLive.StackMapIndex
		pp.PrevLive.StackMapIndex = idx
		p := pp.Prog(obj.APCDATA)
		p.From.SetConst(objabi.PCDATA_StackMapIndex)
		p.To.SetConst(int64(idx))
	}
	if pp.NextLive.IsUnsafePoint != pp.PrevLive.IsUnsafePoint {
		// 发出不安全的点标记。
		pp.PrevLive.IsUnsafePoint = pp.NextLive.IsUnsafePoint
		p := pp.Prog(obj.APCDATA)
		p.From.SetConst(objabi.PCDATA_UnsafePoint)
		if pp.NextLive.IsUnsafePoint {
			p.To.SetConst(objabi.PCDATA_UnsafePointUnsafe)
		} else {
			p.To.SetConst(objabi.PCDATA_UnsafePointSafe)
		}
	}

	p := pp.Next
	pp.Next = pp.NewProg()
	pp.Clear(pp.Next)
	p.Link = pp.Next

	if !pp.Pos.IsKnown() && base.Flag.K != 0 {
		base.Warn("prog: unknown position (line 0)")
	}

	p.As = as
	p.Pos = pp.Pos
	if pp.Pos.IsStmt() == src.PosIsStmt {
		// 如果as可以标记为stmt，则在该pos处清除IsStmt，以供以后的程序使用。
		if LosesStmtMark(as) {
			return p
		}
		pp.Pos = pp.Pos.WithNotStmt()
	}
	return p
}

func (pp *Progs) Clear(p *obj.Prog) {
	obj.Nopout(p)
	p.As = obj.AEND
	p.Pc = pp.PC
	pp.PC++
}

func (pp *Progs) Append(p *obj.Prog, as obj.As, ftype obj.AddrType, freg int16, foffset int64, ttype obj.AddrType, treg int16, toffset int64) *obj.Prog {
	q := pp.NewProg()
	pp.Clear(q)
	q.As = as
	q.Pos = p.Pos
	q.From.Type = ftype
	q.From.Reg = freg
	q.From.Offset = foffset
	q.To.Type = ttype
	q.To.Reg = treg
	q.To.Offset = toffset
	q.Link = p.Link
	p.Link = q
	return q
}

func (pp *Progs) SetText(fn *ir.Func) {
	if pp.Text != nil {
		base.Fatalf("Progs.SetText called twice")
	}
	ptxt := pp.Prog(obj.ATEXT)
	pp.Text = ptxt

	fn.LSym.Func().Text = ptxt
	ptxt.From.Type = obj.TYPE_MEM
	ptxt.From.Name = obj.NAME_EXTERN
	ptxt.From.Sym = fn.LSym
}

// LoseSSTMARK报告带有op as的程序是否在通往DWARF的路上丢失其语句标记。
// 一些操作码的属性在翻译过程中丢失。
// TODO:这是一个关于funcpctab如何在一台PC上组合指令信息的人工制品。
// 应该尝试在那里修复它。
func LosesStmtMark(as obj.As) bool {
	// is_stmt不适用于这些；它对ANOP有效，尽管它不会生成代码。
	return as == obj.APCDATA || as == obj.AFUNCDATA
}
